﻿# pysi.py
"""Module docstring.

This serves as a long usage message.
"""
import os, sys
import getopt
import math
from shapely.geometry import Point
from shapely.geometry import Polygon
from shapely import iterops

class Usage(Exception):
    """Custom Exception class"""
    def __init__(self, msg):
        self.msg = msg

class PyCon(object):
    """Object to parse command line input and arguments:
           argv[0] = inputf   - input file
           argv[1] = outputf  - output file
           argv[2] = kernel   - defines the dispersal kernel: 
                                  1 = standard exp(-alpha*dij)
                                  2 = fatter tailed 1/(1+pow(dij,a))
           argv[3] = alpha    - (>=0) parameter of negative exponential 
                                dispersal kernel
           argv[4] = radius   - the radius within which patches are 
                                considered around focal patch
           argv[5] = b        - scales effect of patch size to emigration
           argv[6] = c        - scales effect of focal patch size to 
                                immigration
        """
    
    def __init__(self, argv=None):

        # Initialize inputlist
        self.inputlist = []         
        
        if argv is None:
            # Discard actual script name
            argv = sys.argv[1:]
    
        try:
            try:
                opts, args = getopt.getopt(argv, "hts:l:g", ["help", "test", 
                                                 "save=", "load=", "gui"])
            except getopt.error, msg:
                raise Usage(msg)
    
            for opt, arg in opts:               
                if opt in ("-h", "--help"):
                    print __doc__
                    return None
                
                elif opt in ("-t", "--test"):
                    try:
                        print '\nYou are using following parameter ' + \
                        'configuration: \n' + \
                            "Input file: "  + "\t" + args[0] + "\n" + \
                            "Output file: " + "\t" + args[1] + "\n" + \
                            "Kernel: "      + "\t" + args[2] + "\n" + \
                            "Alpha: "     + "\t\t" + args[3] + "\n" + \
                            "Radius: "      + "\t" + args[4] + "\n" + \
                            "b: "         + "\t\t" + args[5] + "\n" + \
                            "c: "         + "\t\t" + args[5] + "\n"
                        return None
                    except IndexError:
                        pass
                
                elif opt in ("-s", "--save"):
                    
                    if arg[0] == '=':
                        w_file =  open(arg[1:], 'w')
                        for i in args:  
                            w_file.write(i + " ")
                        w_file.close()
                    else:
                        print >> sys.stderr, 'Specify the name of save file.'
                        print >> sys.stderr, 'for help use --help'
                    return None
                
                elif opt in ("-l", "--load"):
                    
                    if arg[0] == '=':
                        try:
                            file_object = open(arg[1:], 'r')
                            try:
                                args = file_object.readline().split()
                                print args
                            finally:
                                file_object.close()
                        except IOError:
                            print >> sys.stderr, "No such input file!"
                    else:
                        print >> sys.stderr, 'Specify the name of the file ' +\
                                             'to be loaded!'
                        print >> sys.stderr, 'for help use --help'
                    return None                
                
                elif opt in ("-g", "--gui"):
                    print 'Starting graphical user interface...'
                    return None
                
            # Check the number of provided command line arguments
            if len(args) < 7:
                print >> sys.stderr, 'Too few parameters provided --> ' + \
                         str(len(args)) + ' given, 7 needed.'
                print >> sys.stderr, "for help use --help"
                
            else:
                # Populate a dictionary for arguments
                self.argdict = {'inputf': args[0],
                                'outputf': args[1],
                                'kernel': float(args[2]),       
                                'alpha': float(args[3]),
                                'radius': float(args[4]),
                                'b': float(args[5]),
                                'c': float(args[6])}
                                
                # Populate a dictionary for input file                 
                self.indict = {'ID': 0,
                               'x': 1,
                               'y': 2,
                               'a': 3,
                               'o': 4,
                               'p': 5,
                               'si': 6,
                               'nn': 7}
                
                # Read in the input file                
                self.read_file(self.argdict['inputf'])
                

        except Usage, err:
            print >> sys.stderr, err.msg
            print >> sys.stderr, "for help use --help"
            
    def build_points(self):
        """Uses coordinates from inputlist to append
           itself with Point-type representations
           of patches.
        """
        
        # Loop through each patch in inputlist and append each input line
        # with geometry type Point
        for coords in self.inputlist:
            coords.append(Point(float(coords[self.indict['x']]), 
                                float(coords[self.indict['y']])))
    
    def read_file(self, infile):
        """ Reads the input text file line by line and parse input
            values to an array, manipulate instance variable
            inputlist.
            """
        try:
            file_object = open(os.getcwd() + os.sep + infile, 'r')
            try:
                for line in file_object:
                    self.inputlist.append(line.split())
    
            finally:
                file_object.close()
    
        except IOError:
            print >> sys.stderr, "No such input file!"
    
    def build_si(self):
        """Calculates a si value for each patch, the value will be appended
           to inputlist list.
        """
        
        # Initiziate sub_si variable, which will be calculated per patch
        sub_si = 0.0
        min_dis = 0.0
        for patch in self.inputlist:
            # Create a radius-wide buffer around patch Point
            buffer = patch[self.indict['p']].buffer(self.argdict['radius'])
            # TODO: document this well!!!
            nhood = list(iterops.contains
                            (buffer, iter((p[self.indict['p']], p) 
                                for p in self.inputlist
                                    if p[self.indict['ID']] != 
                                       patch[self.indict['ID']]),
                                            True))
            for nbor in nhood:
                dis = patch[self.indict['p']].distance(nbor[self.indict['p']])
                print dis    
                if dis < min_dis:
                    min_dis = dis
                sub_si = sub_si + (nbor[self.indict['o']] * 
                         math.exp(-(self.argdict['alpha']) * dis) * 
                         math.pow(nbor[self.indict['a']], 
                                  self.argdict['b']))
                                
            si = pow(patch[self.indict['a'], self.argdict['c']]) * sub_si
                

if __name__ == '__main__':
    testargs = ['input.dat', 'output.dat', 1, 2, 1, 4, 5]
    con = PyCon(testargs)
    # con = PyCon()
    
    con.build_points()
    con.build_si()